/* Main entry point for the khipu API.

   This file is part of khipu.

   khipu is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

#include <cstdio>
#include <cstdlib>
#include "khipu.hpp"

using namespace khipu;

static inline result<bool>
eval_loop (interpreter *interp, reader& rd,
           stream *outs, io_info& info, object last_expr)
{
  KP_VTRY (outs->write (interp, "khipu> ", 7),
           outs->flush (interp));

  object expr = KP_TRY (rd.read_sexpr ());
  if (expr == EOS)
    return (false);

  object evo = KP_TRY (eval (interp, expr));
  symval(last_expr) = evo;

  KP_VTRY (xwrite (interp, outs, evo, info),
           outs->putb (interp, '\n'));

  return (true);
}

int main (int argc, char **argv)
{
  char buf[4096];

  if (!khipu_init (buf, KP_NELEM (buf)))
    {
      fputs ("failed to initialize khipu runtime\n", stderr);
      return (0);
    }

  interpreter *interp = main_interp;
  valref last_expr (interp, deref (intern (interp, "_")));
  reader rd (interp, in_stream);
  stream *outs = as_stream (out_stream), *errs = as_stream (err_stream);
  io_info info;

  for (info.flags = 0 ; ; )
    {
      auto rv = eval_loop (interp, rd, outs, info, *last_expr);
      if (error_p (rv))
        {
          evh_safeguard eg { interp };
          valref saved_exc (interp, interp->last_err);

          if (interp->throw_frame != 0)
            deref (print_backtrace (interp, interp->throw_frame, errs, info));

          write_exc (interp, errs, *saved_exc, info);
          deref (errs->putb (interp, '\n'));
          deref (errs->flush (interp));
          interp->reset_exc ();
        }
      else if (!*rv)
        break;
    }

  deref (outs->putb (interp, '\n'));
  deref (outs->flush (interp));
  return (0);
}

